#!/bin/bash

BATCH_INSTALL=0

CONF_DIR="/etc/satori"
DOMAIN="satori.example.com"
INTERNAL_DOMAIN="meh"
RULES_REPO="/tmp/rules-repo"
RULES_REPO_SSH="user@domain.com:repo"
RESOLVERS=""
USE_MIRROR=""

DOCKER_MIRROR="https://hub-mirror.c.163.com"

set -e

if [ "$0" != "./install" ]; then
    echo Please change working dir to satori and run ./install
    exit 1
fi


function prepare-docker-images
{
    echo :: Preparing docker images
    echo :: - Copy alarm to build dir
    rm -rf images/alarm/.build
    cp -r ../alarm images/alarm/.build

    echo :: - Copy frontend to build dir
    rm -rf images/nginx/.build
    mkdir -p images/nginx/.build
    cp -a ../frontend images/nginx/.build

    echo :: - Build golang executables
    sudo docker run -it --rm \
        -v /tmp/go-build:/build \
        -v $PWD/..:/src \
        golang:1.12.5-stretch \
        /bin/sh -c '
            set -e
            cd /src
            for c in agent transfer master swcollector; do
                echo :: - - Building $c...
                go build -mod=vendor -o /build/$c github.com/leancloud/satori/$c &
            done
            wait
        '

    mkdir -p ./images/{transfer,master,riemann}/.build
    cp /tmp/go-build/transfer images/transfer/.build
    cp /tmp/go-build/master images/master/.build
    cp /tmp/go-build/agent ../satori-agent

    echo :: - Generate docker-compose.yml
    cat <<EOF > docker-compose.yml
version: '3'
services:
  master:
    build:
      context: images/master
    restart: always
    links: [redis]
    volumes:
      - "$CONF_DIR:/conf"
    ports:
      - "6040:6040"
      - "6041:6041"

  transfer:
    build:
      context: images/transfer
    restart: always
    links: [influxdb, riemann]
    volumes:
      - "$CONF_DIR:/conf"
    ports:
      - "8433:8433"

  alarm:
    build:
      context: images/alarm
    restart: always
    links: [redis]
    volumes:
      - "$RULES_REPO:/satori-conf"
      - "$CONF_DIR:/conf"

  influxdb:
    build:
      context: images/influxdb
    restart: always
    volumes:
      - "influxdb:/var/lib/influxdb"

  grafana:
    build:
      context: images/grafana
    restart: always
    volumes:
      - "$CONF_DIR:/conf"
      - "grafana:/var/lib/grafana"

  redis:
    build:
      context: images/redis
    restart: always
    volumes:
      - "redis:/var/lib/redis"

  nginx:
    build:
      context: images/nginx
    links: [influxdb, grafana, alarm, master]
    restart: always
    volumes:
      - "$CONF_DIR:/conf"
      - "$RULES_REPO:/satori-conf"
    ports:
      - "80:80"
      - "443:443"

  riemann:
    build:
      context: images/riemann
    links: [redis]
    restart: always
    volumes:
      - "$RULES_REPO:/satori-conf"
    ports:
      - "5555:5555"

volumes:
  influxdb: {}
  grafana: {}
  redis: {}
EOF
    echo :: - Build base docker image
    (cd images/base; sudo docker build --build-arg USE_MIRROR=$USE_MIRROR -t satori:base .)
    echo :: - Build app docker images
    sudo $(which docker-compose) build
}


function add-compiled-plugins {
    echo :: Add compiled plugins to rules repo $RULES_REPO
    cp /tmp/go-build/swcollector $RULES_REPO/plugin

    pushd $RULES_REPO >/dev/null
    git add -A :/
    git commit -a -m 'Add compiled plugins'
    popd >/dev/null
}


function prepare-rules-repo
{
    [ -d "$RULES_REPO" ] && return

    echo :: Preparing rules repo $RULES_REPO
    cp -rL ../satori-rules $RULES_REPO
    pushd $RULES_REPO >/dev/null
    rm -rf doc/images
    git init
    git config user.email satori-monitoring@thb.io
    git config user.name Satori
    git add -A :/
    git commit -m 'Initial Satori rules repo'
    git config receive.denyCurrentBranch ignore
    popd >/dev/null
}


function setup-configs
{
    [ -d "$CONF_DIR/.configured" ] && return

    echo :: Setup configs in $CONF_DIR
    if [ -z "$RESOLVERS" ]; then
        echo "!! No resolvers available, please configure RESOLVERS"
        exit 1
    fi

    sudo cp -rT conf $CONF_DIR
    cat <<EOF | sudo bash -
echo "server_name $DOMAIN;" > $CONF_DIR/nginx/name.conf
echo "resolver $RESOLVERS;" > $CONF_DIR/nginx/resolver.conf
EOF

}


function setup-frontend-vars
{
    [ -f "$CONF_DIR/frontend-vars.js" ] && return

    echo :: Setup frontend
    cat <<EOF | sudo bash -c "cat > $CONF_DIR/frontend-vars.js"
var SatoriVars = {
    domain: "$DOMAIN",
    rulesRepo: "$RULES_REPO_SSH",
};
EOF
}

function generate-basic-auth
{
    # [ -f "$CONF_DIR/nginx/basic_auth" ] && return
    BASIC_AUTH_PASSWD=$(cat /proc/sys/kernel/random/uuid)

    echo :: Generate basic_auth file for nginx...
    sudo htpasswd -cb $CONF_DIR/nginx/basic_auth satori $BASIC_AUTH_PASSWD
}

function setup-bitbar-plugin
{
    [ -z "$BASIC_AUTH_PASSWD" ] && return

    echo ":: Setup your BitBar plugin (which resides in your rules repo ${RULES_REPO})"
    cp ../alarm/bitbar-plugin.py $RULES_REPO/bitbar-plugin.py
    sed -i "s|CREDENTIAL|satori:$BASIC_AUTH_PASSWD|g" $RULES_REPO/bitbar-plugin.py
    sed -i "s|DOMAIN|$DOMAIN|g" $RULES_REPO/bitbar-plugin.py
    pushd $RULES_REPO >/dev/null
    chmod +x bitbar-plugin.py
    git add bitbar-plugin.py
    git commit -a -m 'Update bitbar plugin'
    popd >/dev/null
}

function generate-agent-config
{
    echo :: Generate agent config ../agent-cfg.yaml
    pushd .. >/dev/null
    cp agent/cfg.example.yaml agent-cfg.yaml
    sed -i "s|INTERNAL_DOMAIN|$INTERNAL_DOMAIN|g" agent-cfg.yaml
    sed -i "s|DOMAIN|$DOMAIN|g" agent-cfg.yaml
    sed -i "s|PASSWD|$BASIC_AUTH_PASSWD|g" agent-cfg.yaml
    popd >/dev/null
}

function detect-accidental-run
{
    if [ -f "$CONF_DIR/.configured" ]; then
        whiptail --title Satori --msgbox "Satori is already configured.\nIf you insist, please remove /etc/satori and try again." 8 60
        exit 1
    fi

    if [ "$(whoami)" == "root" ]; then
        if ! whiptail --title Satori --yesno "You are running install as root.\nThis is highly discouraged since rules repo should have R/W access for regular user.\nContinue with root?" 10 60; then
            exit 1
        fi
        if ! whiptail --title Satori --yesno "Please double check.\nYou are configuring as root.\nARE YOU SURE?" 9 60; then
            exit 1
        fi
    fi
}

function ensure-docker
{
    if ! which docker > /dev/null 2>&1; then
        if whiptail --title Satori --yesno "It seems that Docker is unavailable.\nDo you want to install Docker?" 8 60; then
            ensure-docker-auto
        else
            echo '!! Please install Docker.'
        fi
        exit 1
    fi
}

function ensure-docker-compose
{
    if ! which docker-compose > /dev/null 2>&1; then
        if whiptail --title Satori --yesno "It seems that Docker Compose is unavailable.\nDo you want to install Docker Compose?" 8 60; then
            ensure-docker-compose-auto
        else
            echo '!! Please install Docker Compose.'
        fi
        exit 1
    fi
}

function ensure-htpasswd
{
    if ! which htpasswd > /dev/null 2>&1; then
        if whiptail --title Satori --yesno "It seems that htpasswd is unavaliable.\nDo you want to install relevant packages?" 8 60; then
            ensure-htpasswd-auto
        else
            echo '!! Installer requires htpasswd to function.'
        fi
        exit 1
    fi
}

function ensure-docker-auto
{
    if ! which docker > /dev/null 2>&1; then
        if ! [ -z "$USE_MIRROR" ]; then
            curl -sSL https://get.daocloud.io/docker | sh /dev/stdin --mirror Aliyun
        else
            curl -sSL https://get.docker.com/ | sh
        fi
    fi
}

function ensure-docker-compose-auto
{
    if ! which docker-compose > /dev/null 2>&1; then
        if ! [ -z "$USE_MIRROR" ]; then
            curl -L "https://mirrors.aliyun.com/docker-toolbox/linux/compose/1.21.2/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose
        else
            curl -L "https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose
        fi
        sudo mv /tmp/docker-compose /usr/local/bin/docker-compose
        sudo chmod +x /usr/local/bin/docker-compose
        echo ':: Docker Compose installed to /usr/local/bin/docker-compose'
    fi
}

function ensure-htpasswd-auto
{
    if ! which htpasswd > /dev/null 2>&1; then
        if which apt-get > /dev/null 2>&1; then
            sudo apt-get -y install apache2-utils
            echo ':: apache2-utils installed'
        elif which yum > /dev/null 2>&1; then
            sudo yum install -y httpd-tools
            echo ':: httpd-tools installed'
        else
            echo "!! Don't know how to install htpasswd. Sorry."
            exit 1
        fi
    fi
}

function setup-docker-mirror
{
    [ -z "$USE_MIRROR" ] && return
    cat <<EOF | sudo python -
import sys
import json

registry = "$DOCKER_MIRROR"

try:
    content = open('/etc/docker/daemon.json').read()
except Exception:
    content = '{}'

v = json.loads(content)
k = 'registry-mirrors'
if k not in v:
    v[k] = [registry]
elif registry not in v[k]:
    v[k].append(registry)
else:
    sys.exit(0)

open('/etc/docker/daemon.json', 'w').write(json.dumps(v))
EOF
    sudo systemctl reload docker || true

    echo ":: $DOCKER_MIRROR added to Docker registry-mirrors"
}

function save-parameters
{
    sudo touch $CONF_DIR/.configured
    sudo chmod 0666 $CONF_DIR/.configured
    cat <<EOF > $CONF_DIR/.configured
USE_MIRROR="$USE_MIRROR"
DOMAIN="$DOMAIN"
INTERNAL_DOMAIN="$INTERNAL_DOMAIN"
RULES_REPO="$RULES_REPO"
RULES_REPO_SSH="$RULES_REPO_SSH"
RESOLVERS="$RESOLVERS"
EOF
    sudo chmod 0644 $CONF_DIR/.configured
}

function go-live
{
    sudo $(which docker-compose) up -d
}

function ask-for-updating-containers
{
    if whiptail --title Satori --yesno "Do you want to restart Satori containers now?" 8 60; then
        go-live
    fi
}

function ask-for-variables
{
    USE_MIRROR=""
    if whiptail --title Satori --yesno "Do you want to use various mirrors from mainland China?\nThis will speed up installation process if your server resides in mainland China." 9 60; then
        USE_MIRROR=1
    fi

    DOMAIN=""
    whiptail --title Satori --inputbox "What's your public accessable domain name for this machine?\nThis domain is used to setup nginx configurations." 10 70 $(hostname -f) 2>/tmp/satori-setting
    DOMAIN=$(cat /tmp/satori-setting)
    if [ -z "$DOMAIN" ]; then
        echo '!! Aborting.'
        exit 1
    fi

    INTERNAL_DOMAIN=""
    whiptail --title Satori --inputbox "What's your internal accessable domain name or IP address for this machine?\nThis is used for transferring metrics by satori-agent.\nUse default if unsure." 10 70 "$DOMAIN" 2>/tmp/satori-setting
    INTERNAL_DOMAIN=$(cat /tmp/satori-setting)
    if [ -z "$INTERNAL_DOMAIN" ]; then
        echo '!! Aborting.'
        exit 1
    fi

    RULES_REPO=""
    whiptail --title Satori --inputbox "Where do you want to put your rules repo?\nRules repo is a git repository where your rules and plugins stored, it should be easily accessable by git clone.\nUse default if unsure." 10 70 "$HOME/satori-rules" 2>/tmp/satori-setting
    RULES_REPO=$(cat /tmp/satori-setting)
    if [ -z "$RULES_REPO" ]; then
        echo '!! Aborting.'
        exit 1
    fi

    RULES_REPO_SSH=""
    whiptail --title Satori --inputbox "What's your public git-clonable address for your rules repo?\nThis value will show on frontend landing page." 10 70 "$(whoami)@$DOMAIN:$RULES_REPO" 2>/tmp/satori-setting
    RULES_REPO_SSH=$(cat /tmp/satori-setting)
    if [ -z "$RULES_REPO_SSH" ]; then
        echo '!! Aborting.'
        exit 1
    fi

    RESOLVERS="$(grep ^nameserver /etc/resolv.conf | grep -v '^nameserver  *127' | awk '{print $2}' | xargs)"
    if [ -z "$RESOLVERS" ]; then
        whiptail --title Satori --inputbox "Please provide comma separated DNS server IP address(es). Remove 'ipv6=off' if your network is IPv6 capable." 8 70 "223.5.5.5,119.29.29.29,ipv6=off" 2>/tmp/satori-setting
        RESOLVERS=$(cat /tmp/satori-setting | sed 's/,/ /g')
        if [ -z "$RESOLVERS" ]; then
            echo '!! Aborting.'
            exit 1
        fi
    fi
}

function init-influxdb
{
    echo ":: Create initial InfluxDB database"
    sudo docker exec $(sudo docker ps | grep satori_influxdb | awk '{print $1}') influx -execute 'CREATE DATABASE satori;'
}

function finish
{
    echo
    echo ":: Satori is installed."
    echo ":: Config: $CONF_DIR"
    echo ":: Rules repo: $RULES_REPO ($RULES_REPO_SSH)"
    echo ":: Your initial Web interface credentials:"
    echo "     Username: satori"
    echo "     Password: $BASIC_AUTH_PASSWD"
    echo
    echo ":: When upgrading, please run:"
    echo "     $ git pull"
    echo "     $ ./install rebuild"
    echo "     $ sudo docker-compose up -d"
    echo
    echo ":: When restarting components, please run:"
    echo "     $ sudo docker-compose restart <component>"
    echo "     $ sudo docker-compose restart alarm"
    echo "     $ sudo docker-compose restart transfer"
    echo "     $ sudo docker-compose restart ..."
}

if [ "$1" == "rebuild" ]; then
    if ! . $CONF_DIR/.configured 2>/dev/null; then
        echo !! Failed to load parameters, you should install first.
        exit 1
    fi
    prepare-docker-images
    ask-for-updating-containers

elif [ "$1" = "-f" -a -f "$2" ]; then
    echo :: Performing batch install

    . $2

    [ -z "$USE_MIRROR" ] && echo "!! Missing conf USE_MIRROR" && exit 1
    [ -z "$DOMAIN" ] && echo "!! Missing conf DOMAIN" && exit 1
    [ -z "$INTERNAL_DOMAIN" ] && echo "!! Missing conf INTERNAL_DOMAIN" && exit 1
    [ -z "$RULES_REPO" ] && echo "!! Missing conf RULES_REPO" && exit 1
    [ -z "$RULES_REPO_SSH" ] && echo "!! Missing conf RULES_REPO_SSH" && exit 1

    ensure-docker-auto
    ensure-docker-compose-auto
    ensure-htpasswd-auto
    setup-configs
    prepare-rules-repo
    # setup-docker-mirror  # seems official registry is fast enough now
    prepare-docker-images
    add-compiled-plugins
    generate-basic-auth
    setup-bitbar-plugin
    setup-frontend-vars
    generate-agent-config
    save-parameters
    if ! go-live; then
        echo !! Something goes wrong while starting docker containers, continuing anyway
    fi
    sleep 5
    init-influxdb
    finish

elif [ "$1" = "" ]; then
    ensure-docker
    ensure-docker-compose
    ensure-htpasswd
    detect-accidental-run
    ask-for-variables

    setup-configs
    prepare-rules-repo
    # setup-docker-mirror  # seems official registry is fast enough now
    prepare-docker-images
    add-compiled-plugins
    generate-basic-auth
    setup-bitbar-plugin
    setup-frontend-vars
    generate-agent-config
    save-parameters
    if ! go-live; then
        echo !! Something goes wrong while starting docker containers, continuing anyway
    fi
    sleep 5
    init-influxdb
    finish
else
    echo "!! Don't know how to proceed your command."
    exit 1
fi
